package com.karson.common.util;

import jakarta.annotation.Resource;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

//    private static RedisTemplate<String, Object> redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
    // =============== common ==========================
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return boolean
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 通用 Pipeline 执行器
     *
     * @param callback 回调函数，内部通过 connection 执行 Redis 操作
     * @return
     */
    public List<Object> executePipeline(Consumer<RedisConnection> callback) {
        return redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            callback.accept(connection);
            return null;
        });
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效 失效时间为负数，说明该主键未设置失效时间（失效时间默认为-1）
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false 不存在
     */
    public boolean exists(String key){
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除对应的value
     * @param key string key
     */
    public void delete(String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    // =============== string ==========================
    /**
     * 写入缓存
     * @param key string key
     * @param value string value
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 写入缓存带有效期(默认时间单位:秒)
     * @param key string key
     * @param value string value
     * @param expireTime Long 过期时间
     * @return boolean
     */
    public boolean set(String key, Object value, Long expireTime) {
        try {
            if (expireTime > 0) {
                redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 写入缓存带有效期
     * @param key string key
     * @param value string value
     * @param expireTime Long 过期时间
     * @param timeUnit TimeUnit 时间格式
     * @return boolean
     */
    public boolean set(String key, Object value, Long expireTime, TimeUnit timeUnit) {
        try {
            redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 读取缓存
     * @param key string key
     * @return T
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key) {
        return (T) redisTemplate.opsForValue().get(key);
    }

    /**
     * 哈希添加
     * @param key string key
     * @param hashKey Object hashKey
     * @param value Object value
     */

    public void hmSet(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 哈希删除
     * @param key string key
     * @param hashKey Object hashKey
     */

    public void hmDelete(String key, Object hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 哈希获取数据
     * @param key string key
     * @param hashKey Object hashKey
     */
    public Object hmGet(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    /**
     * Hash 自增
     * @param key redis的key
     * @param hashKey hash里的field
     * @param delta 增量(大于0表示自增，小于0表示自减)
     * @return 自增后的结果
     */
    public long hincrBy(String key, String hashKey, long delta) {
        if (delta == 0) {
            throw new RuntimeException("增量不能为0");
        }
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }
    /**
     * 获取整个 Hash
     * @param key redis的key
     * @return Map<String, Object>
     */
    public Map<Object, Object> hmGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 哈希数量
     * @param key string key
     */
    public Long getHashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 列表添加左边添加
     * @param k string key
     * @param v Object v
     * @author Mr.Zhang
     * @since 2020-04-13
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.leftPush(k, v);
    }

    /**
     * 从右边拿出来一个
     * @param k string key
     * @param t Long 超时秒数
     */
    public Object getRightPop(String k, Long t){
        return redisTemplate.opsForList().rightPop(k, t, TimeUnit.SECONDS);
    }

    /**
     * 列表获取数量
     * @param k string key
     * @return Long
     */
    public Long getListSize(String k) {
        return redisTemplate.opsForList().size(k);
    }

    /**
     * 列表获取
     * @param k string key
     * @param l long l
     * @param l1 long l1
     * @return List<Object>
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 集合添加
     * @param key string key
     * @param value Object value
     */
    public void add(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }
    /**
     * setnx 操作（仅当 key 不存在时才设置值）
     * @param key redis key
     * @param value 值
     * @param expireTime 过期时间（秒），传 <=0 表示不过期
     * @return true 设置成功（之前不存在），false 设置失败（key 已存在）
     */
    public boolean setNx(String key, Object value, long expireTime) {
        try {
            if (expireTime > 0) {
                return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS));
            } else {
                return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 集合获取
     * @param key string key
     * @return Set<Object>
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }
    /**
     * 从Set中移除元素
     */
    public boolean removeMember(String key, Object value) {
        Long result = redisTemplate.opsForSet().remove(key, value);
        return result != null && result > 0;
    }
    /**
     * 判断集合中是否存在某个值
     * @param key redis的key
     * @param value 值
     * @return true 存在，false 不存在
     */
    public boolean sIsMember(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 有序集合添加
     * @param key string key
     * @param value Object value
     * @param score double scoure
     */
    public void zAdd(String key, Object value, double score) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, score);
    }
    /**
     * 批量添加有序集合元素（ZSet）
     * @param key Redis 的 key
     * @param valueScoreMap 元素及对应的分数 Map
     *                      key = 成员对象，value = 分数
     * @return 成功添加的元素数量
     */
    public long zAddBatch(String key, Map<Object, Double> valueScoreMap) {
        if (valueScoreMap == null || valueScoreMap.isEmpty()) {
            return 0;
        }

        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        Set<ZSetOperations.TypedTuple<Object>> tuples = valueScoreMap.entrySet().stream()
                .map(e -> new DefaultTypedTuple<>(e.getKey(), e.getValue()))
                .collect(Collectors.toSet());

        Long addedCount = zset.add(key, tuples);
        return addedCount == null ? 0 : addedCount;
    }
    public Double zScore(String key, Integer entityId) {
       return redisTemplate.opsForZSet().score(key, entityId);
    }

    /**
     * 有序集合获取
     * @param key string key
     * @param score double scoure
     * @return Set<Object>
     */
    public Set<Object> rangeByScore(String key, double score, double score1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, score, score1);
    }
    /**
     * 获取 ZSet 中最新的 N 条数据（按 score 倒序）
     * @param key redis key
     * @param count 需要获取的条数
     * @return 元素集合（按倒序返回）
     */
    public Map<Object, Double> getLatest(String key, long count) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        Set<ZSetOperations.TypedTuple<Object>> tuples = zset.reverseRangeWithScores(key, 0, count - 1);

        if (tuples == null || tuples.isEmpty()) {
            return Collections.emptyMap();
        }

        return tuples.stream()
                .collect(Collectors.toMap(
                        ZSetOperations.TypedTuple::getValue,
                        ZSetOperations.TypedTuple::getScore,
                        (oldV, newV) -> oldV,   // 如果重复 key，保留旧值
                        LinkedHashMap::new      // 保留原顺序（按 score 排序）
                ));
    }
    /**
     * 获取 ZSet 分页数据（按 score 倒序）
     * @param key redis key
     * @param page 页码，从 1 开始
     * @param pageSize 每页条数
     * @return 元素集合（按倒序返回）
     */
    public Set<Object> getPage(String key, long page, long pageSize) {
        if (page < 1 || pageSize < 1) {
            throw new IllegalArgumentException("page 和 pageSize 必须大于0");
        }
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        long start = (page - 1) * pageSize;
        long end = page * pageSize -1;
        return zset.reverseRange(key, start, end);
    }
    /**
     * 随机从 ZSet 中抽取指定数量的 member
     * @param key ZSet key
     * @param count 抽取数量
     * @param <T> member 类型
     * @return 抽取结果集合
     */
    public <T> List<T> randomMembers(String key, int count) {
        if (count <= 0) {
            return Collections.emptyList();
        }

        ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
        long size = Optional.ofNullable(zSetOps.size(key)).orElse(0L);

        if (size == 0) {
            return Collections.emptyList();
        }

        // 随机索引，利用 reverseRange + 随机选
        Set<Object> members = new HashSet<>();
        Random random = new Random();

        List<Object> allMembers = new ArrayList<>(zSetOps.range(key, 0, size - 1));

        while (members.size() < count && members.size() < allMembers.size()) {
            members.add(allMembers.get(random.nextInt(allMembers.size())));
        }

        return members.stream()
                .map(o -> (T) o)
                .collect(Collectors.toList());
    }
    /**
     * 删除整个 ZSet
     * @param key ZSet 的 key
     */
    public void deleteZSet(String key) {
        try {
            // 直接删除，不依赖 exists 判断
            redisTemplate.delete(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取 ZSet 中元素总数
     * @param key redis key
     * @return 元素数量
     */
    public long size(String key) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        Long size = zset.zCard(key);
        return size == null ? 0 : size;
    }

    /**
     * 移除 ZSet 中下标区间的元素（可用于限制长度，删除最旧的数据）
     * @param key redis key
     * @param start 开始下标
     * @param end 结束下标
     */
    public void removeRange(String key, long start, long end) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.removeRange(key, start, end);
    }

    /**
     * 移除 ZSet 中 min — max 区间的元素
     * @param key
     * @param min
     * @param max
     */
    public void removeRangeByScore(String key, double min, double max) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.removeRangeByScore(key,min,max);
    }
    /**
     * 移除 ZSet 中下标区间的元素（可用于限制长度，删除最旧的数据）
     * @param key redis key
     * @param value 成员
     */
    public void zRemove(String key, Object value) {
        redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 增加或减少推荐分数（点赞/点踩等场景）
     */
    public void incrementScore(String key, Integer itemId, double delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        redisTemplate.opsForZSet().incrementScore(key, itemId, delta);
    }
    /**
     * 递增
     * @param key 键
     * @return long
     */

    public long incrAndCreate(String key){
        if (!exists(key)) {
            set(key, 1);
            return 1;
        }
        return incr(key, 1L);
    }

    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return long
     */
    public long incr(String key, long delta){
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

}